Skip to content

Conversation

@graycreate
Copy link
Member

Summary

  • Add V2EX preset stylesheet with styles matching the Android app's CSS
  • Add TableStyle and HorizontalRuleStyle components for more control
  • Add rendering support for underline, superscript, and subscript HTML tags

Changes

V2EX Preset Stylesheet

Matches the Android app's v2er.css and font.css:

  • Heading sizes: h1=22, h2=18, h3=16, h4=15, h5=12, h6=10 (matching Android)
  • Medium font weight for headings (500 in Android)
  • Body text color: #555555 (gray) for light mode
  • Link color: #778087 (grayish V2EX style)
  • Blockquote: 3px left border with rgba(126, 126, 126, 0.5) color
  • Code: 80% font size (13px) relative to body (16px)
  • Dark mode support matching Android's .dark CSS classes

New Style Components

  • TableStyle: header font weight, separator color/width, cell padding, alternate row color
  • HorizontalRuleStyle: color, height, vertical padding

HTML Tag Rendering

  • <u> underline rendering with underlineStyle = .single
  • <sup> superscript with 70% font size and positive baseline offset
  • <sub> subscript with 70% font size and negative baseline offset

Test plan

  • Build successfully
  • Install and launch on device
  • Test V2EX posts with tables, code blocks, blockquotes
  • Verify dark mode styling
  • Test posts with underline, superscript, subscript content

🤖 Generated with Claude Code

Add V2EX preset stylesheet with styles matching the Android app:
- Smaller heading sizes (h1: 22, h2: 18, h3: 16, h4: 15, h5: 12, h6: 10)
- Medium font weight for headings (matching Android's 500)
- Body text color #555555 (gray) for light mode
- Link color #778087 (grayish) matching V2EX web style
- Blockquote: 3px left border with subtle background
- Code: 80% font size (13px) relative to body

Add new styling components:
- TableStyle: header font weight, separator color, cell padding
- HorizontalRuleStyle: color, height, vertical padding

Add rendering support for HTML tags:
- <u> underline with underlineStyle attribute
- <sup> superscript with smaller font and positive baseline offset
- <sub> subscript with smaller font and negative baseline offset

Update MarkdownRenderer to use stylesheet styles for:
- Horizontal rules
- Table headers and separators

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copilot AI review requested due to automatic review settings December 1, 2025 10:29
@github-actions github-actions bot added the size/L label Dec 1, 2025
Copilot finished reviewing on behalf of graycreate December 1, 2025 10:33
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the RichView rendering system to match the Android app's styling by adding a V2EX preset stylesheet, introducing new style components for tables and horizontal rules, and adding support for underline, superscript, and subscript HTML tags.

Key Changes

  • Added TableStyle and HorizontalRuleStyle components with configurable properties for consistent table and horizontal rule rendering
  • Implemented V2EX preset stylesheet with colors, font sizes, and spacing matching the Android app's CSS (e.g., body text at 16px with #555555 color, heading sizes ranging from 10-22px)
  • Added HTML tag rendering support for <u> (underline), <sup> (superscript with 70% font size and baseline offset), and <sub> (subscript with 70% font size and negative baseline offset)

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
V2er/Sources/RichView/Renderers/MarkdownRenderer.swift Adds horizontal rule rendering function using new stylesheet property; implements HTML tag parsing for underline, superscript, and subscript; updates table rendering to use new TableStyle properties
V2er/Sources/RichView/Models/RenderStylesheet.swift Defines TableStyle and HorizontalRuleStyle structs; adds V2EX preset stylesheet with Android-matching styles; extends default stylesheet with table and horizontal rule styles
CLAUDE.md Documents Android project location reference

Comment on lines +356 to +414
// Check for underline (<u>text</u>)
if let underlineMatch = currentText.firstMatch(of: /<u>(.+?)<\/u>/) {
// Add text before underline
let beforeRange = currentText.startIndex..<underlineMatch.range.lowerBound
if !beforeRange.isEmpty {
result.append(renderPlainText(String(currentText[beforeRange])))
}

// Add underlined text
var underlineText = AttributedString(String(underlineMatch.1))
underlineText.font = .system(size: stylesheet.body.fontSize)
underlineText.foregroundColor = stylesheet.body.color.uiColor
underlineText.underlineStyle = .single
result.append(underlineText)

// Continue with remaining text
currentText = String(currentText[underlineMatch.range.upperBound...])
continue
}

// Check for superscript (<sup>text</sup>)
if let supMatch = currentText.firstMatch(of: /<sup>(.+?)<\/sup>/) {
// Add text before superscript
let beforeRange = currentText.startIndex..<supMatch.range.lowerBound
if !beforeRange.isEmpty {
result.append(renderPlainText(String(currentText[beforeRange])))
}

// Add superscript text (smaller font, baseline offset)
var supText = AttributedString(String(supMatch.1))
supText.font = .system(size: stylesheet.body.fontSize * 0.7)
supText.foregroundColor = stylesheet.body.color.uiColor
supText.baselineOffset = stylesheet.body.fontSize * 0.3
result.append(supText)

// Continue with remaining text
currentText = String(currentText[supMatch.range.upperBound...])
continue
}

// Check for subscript (<sub>text</sub>)
if let subMatch = currentText.firstMatch(of: /<sub>(.+?)<\/sub>/) {
// Add text before subscript
let beforeRange = currentText.startIndex..<subMatch.range.lowerBound
if !beforeRange.isEmpty {
result.append(renderPlainText(String(currentText[beforeRange])))
}

// Add subscript text (smaller font, negative baseline offset)
var subText = AttributedString(String(subMatch.1))
subText.font = .system(size: stylesheet.body.fontSize * 0.7)
subText.foregroundColor = stylesheet.body.color.uiColor
subText.baselineOffset = -stylesheet.body.fontSize * 0.2
result.append(subText)

// Continue with remaining text
currentText = String(currentText[subMatch.range.upperBound...])
continue
}
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new underline, superscript, and subscript HTML tag rendering features lack test coverage. Since V2erTests/RichView/MarkdownRendererTests.swift has comprehensive tests for other inline formatting features (bold, italic, code, links, mentions, strikethrough, highlights), these new features should also have corresponding test cases to ensure they work correctly.

Copilot uses AI. Check for mistakes.
Comment on lines 267 to 289
public struct TableStyle: Equatable {
public var headerFontWeight: Font.Weight
public var headerBackgroundColor: Color
public var cellPadding: CGFloat
public var separatorColor: Color
public var separatorWidth: CGFloat
public var alternateRowColor: Color?

public init(
headerFontWeight: Font.Weight = .semibold,
headerBackgroundColor: Color = .clear,
cellPadding: CGFloat = 8,
separatorColor: Color = Color.gray.opacity(0.3),
separatorWidth: CGFloat = 0.5,
alternateRowColor: Color? = nil
) {
self.headerFontWeight = headerFontWeight
self.headerBackgroundColor = headerBackgroundColor
self.cellPadding = cellPadding
self.separatorColor = separatorColor
self.separatorWidth = separatorWidth
self.alternateRowColor = alternateRowColor
}
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Several properties in TableStyle (headerBackgroundColor, cellPadding, separatorWidth, alternateRowColor) are defined but not currently used in the table rendering logic. Consider either implementing these properties in the rendering code or adding documentation comments explaining that they are reserved for future use.

Copilot uses AI. Check for mistakes.
Comment on lines 293 to 307
public struct HorizontalRuleStyle: Equatable {
public var color: Color
public var height: CGFloat
public var verticalPadding: CGFloat

public init(
color: Color = Color(hex: "#f4f2f2"),
height: CGFloat = 0.8,
verticalPadding: CGFloat = 8
) {
self.color = color
self.height = height
self.verticalPadding = verticalPadding
}
}
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The height and verticalPadding properties in HorizontalRuleStyle are defined but not currently used in the horizontal rule rendering logic (which uses a fixed character-based rendering approach). Consider either implementing these properties in the rendering code or adding documentation comments explaining that they are reserved for future use when the rendering approach changes.

Copilot uses AI. Check for mistakes.
CLAUDE.md Outdated
- **CHANGELOG.md is required** for all releases - the build will fail if the current version is missing from the changelog
- Always install to Gray'iPhone if it connected, otherwise install to simulator
- Always install to Gray'iPhone if it connected, otherwise install to simulator
- the coresponding android project located in ../v2er-android
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "coresponding" should be spelled "corresponding".

Suggested change
- the coresponding android project located in ../v2er-android
- the corresponding android project located in ../v2er-android

Copilot uses AI. Check for mistakes.
} else if line.starts(with: "---") {
// Horizontal rule
attributedString.append(AttributedString("—————————————\n"))
attributedString.append(renderHorizontalRule())
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The horizontal rule character has been changed from "—" (em dash) to "─" (box drawing character), but the existing test in MarkdownRendererTests.swift line 290 still checks for "—". This will cause the test to fail. Consider updating the test to check for "─" instead.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Code Coverage Report ❌

Current coverage: 26.44%

- Fix typo in CLAUDE.md ("coresponding" → "corresponding")
- Update horizontal rule test to use box drawing character (─)
- Add tests for underline, superscript, and subscript rendering
- Add documentation for reserved style properties in TableStyle
  and HorizontalRuleStyle

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@github-actions github-actions bot added size/L and removed size/L labels Dec 1, 2025
@graycreate graycreate merged commit b83e847 into main Dec 1, 2025
6 checks passed
@graycreate graycreate deleted the feature/optimize-richview-styles branch December 1, 2025 10:41
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Code Coverage Report ❌

Current coverage: 27.98%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants